import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.HashMap;

class cnnode extends Thread{
	public static void main(String[] args) throws SocketException{
		
		try{
			selfPort = Integer.parseInt(args[0]);
			if(selfPort<1024){
				System.out.println("Self port must be greater than 1024. Program terminates.");
				System.exit(0);
			}
		}catch(Exception e){
			System.out.println("Self port must be an integer. Program terminates.");
			System.exit(0);
		}
		
		if(!args[1].equals("receive")){
			System.out.println("args[1] must be \"receive\". Program terminates.");
			System.exit(0);
		}
		
		else{
			int i = 2;
			while(!args[i].equals("send")){
				int port = Integer.parseInt(args[i]);
				double distance = Double.parseDouble(args[++i]);
				receiveMap.put(port, distance);//this map indicate receive data from who with what probability
				neighborList.put(port, 0.00);//neighbors
				i++;
			}
			i++;//move to the next argument after send
			while(true){
				if(i == args.length){
					break;
				}
				else if(args[i].equals("last")){
					isLast = true;
					break;
				}
				else{
					int port = Integer.parseInt(args[i++]);
					probeCounter.put(port, new counter());//create a probe counter for this port
					neighborList.put(port, 0.00);
				}
			}
		}
		
		DatagramSocket clientSocket = new DatagramSocket(selfPort);
			
		if(isLast){//the last node broadcast it's initial dv table
			String info = "";
			for(Integer i:neighborList.keySet()){
				info = info + "#" +i+ "#" + String.valueOf(0.00);
			}	
			info = info.substring(1);
			sendRoutingTable(info, clientSocket);
		
		}
		
		while(true){
			
			byte[] receiveData = new byte[1024];
			DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
			
			try {
				clientSocket.receive(receivePacket);
			}
			catch (IOException e1) {
				e1.printStackTrace();	
			}
			
			new dataProcessing(clientSocket, receivePacket);//create a new thread to process this packet
			
			if(!hasProbeSender){//after receiving a packet, create a probe sender if there isn't one
				new probeSender(clientSocket);
				hasProbeSender = true;
			}	
			
			
		}
	}
	
	
	
	private static void sendRoutingTable(String s, DatagramSocket socket) {
		
		for(Integer i:neighborList.keySet()){
			String str = s;
			if(probeCounter.containsKey(i)){
				str = String.valueOf(System.currentTimeMillis())+ "#" + String.valueOf(selfPort)+";"+String.valueOf(0.00)+"#info#"+str; 
			}
			else{
				str = String.valueOf(System.currentTimeMillis())+ "#" + String.valueOf(selfPort)+"#info#"+str; 
			}
			
			
			byte[] sendData = str.getBytes();
			
			try {
				DatagramPacket SendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("localHost"), i);
				socket.send(SendPacket);
				System.out.println(currentTime()+"Message sent from Node "+selfPort+" to Node "+ i+"["+str+"]");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public static String currentTime() {
        String s = String.valueOf(System.currentTimeMillis());
        return "["+s.substring(0, 10)+"."+s.substring(10)+"]";
	}
	
	public static void dispalayRoutingTable(){//
		
		System.out.println(currentTime()+"Node "+selfPort+" Routing Table");		
		
		for(Integer i:dv.keySet()){
			if(i == selfPort){
				continue;
			}
			if(i == dv.get(i).nextHop){
				System.out.println("- ("+(double)Math.round(dv.get(i).distance*100)/100+") -> Node "+i);
			}
			else{
				System.out.println("- ("+(double)Math.round(dv.get(i).distance*100)/100+") -> Node "+i+"; Next hop -> Node "+ dv.get(i).nextHop);
			}
			
		}
	}



	public static int selfPort;
	public static HashMap<Integer, Double> receiveMap = new HashMap<Integer, Double>();
	public static boolean isLast ,isUpdated;
	public static int receiveCount = 0;
	public static boolean hasProbeSender = false;
    public static HashMap<Integer, Double> neighborList = new HashMap<Integer, Double>();
	public static HashMap<Integer, counter> probeCounter = new HashMap<Integer, counter>();
	public static HashMap<Integer, table> dv = new HashMap<Integer, table>();
	public static HashMap<Integer, HashMap<Integer, Double>> neighborDv = new HashMap<Integer, HashMap<Integer, Double>>();
}